package com.flowable.convert.task;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.common.core.domain.entity.SysDept;
import com.common.core.domain.entity.SysUser;
import com.flowable.common.*;
import com.flowable.common.object.BeanUtils;
import com.flowable.controller.oa.vo.AdminUserRespDTO;
import com.flowable.controller.oa.vo.DeptRespDTO;
import com.flowable.controller.oa.vo.UserSimpleBaseVO;
import com.flowable.domain.instance.BpmApprovalDetailRespVO;
import com.flowable.domain.instance.BpmProcessInstanceBpmnModelViewRespVO;
import com.flowable.domain.instance.BpmProcessInstanceRespVO;
import com.flowable.domain.vo.BpmProcessDefinitionRespVO;
import com.flowable.domain.vo.BpmTaskRespVO;
import com.flowable.event.BpmProcessInstanceStatusEvent;
import com.flowable.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
import com.flowable.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;

import java.util.*;

import static com.common.utils.CollectionUtils.convertList;
import static com.common.utils.CollectionUtils.convertSet;

/**
 * 流程实例 Convert
 *
 * @author 芋道源码
 */
@Mapper
public interface BpmProcessInstanceConvert {

    BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class);

    default PageResult<BpmProcessInstanceRespVO> buildProcessInstancePage(PageResult<HistoricProcessInstance> pageResult,
                                                                          Map<String, ProcessDefinition> processDefinitionMap,
                                                                          Map<String, List<Task>> taskMap,
                                                                          Map<Long, SysUser> userMap,
                                                                          Map<Long, SysDept> deptMap) {
        PageResult<BpmProcessInstanceRespVO> vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class);
        for (int i = 0; i < pageResult.getList().size(); i++) {
            BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i);
            respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i)));
            MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(),
                    processDefinition -> respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)));
            //MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName()));
             List<Task> bbb=  taskMap.get(respVO.getId());
            respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class));
            // user
            if (userMap != null) {
                SysUser startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId()));
                if (startUser != null) {
                    respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
                    MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getDeptName()));
                }
            }
        }
        return vpPageResult;
    }

    default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance,
                                                          ProcessDefinition processDefinition,
                                                          SysUser startUser,
                                                          DeptRespDTO dept) {
        BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class);
        respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
        respVO .setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance));
        // definition
        respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class));

        // user
        if (startUser != null) {
            respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class));
            if (dept != null) {
                respVO.getStartUser().setDeptName(dept.getName());
            }
        }
        return respVO;
    }



        default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) {
            BpmProcessInstanceStatusEvent bpmProcessInstanceStatusEvent = new BpmProcessInstanceStatusEvent(source);
            bpmProcessInstanceStatusEvent.setId(instance.getId());
            bpmProcessInstanceStatusEvent.setStatus(status);

            bpmProcessInstanceStatusEvent.setProcessDefinitionKey(instance.getProcessDefinitionKey());
            bpmProcessInstanceStatusEvent.setBusinessKey(instance.getBusinessKey());
            return  bpmProcessInstanceStatusEvent;
//            return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status)
//                    .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey());
        }
    //
        default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance,String reason, SysUser sysUser) {
             BpmMessageSendWhenProcessInstanceApproveReqDTO bpmMessageSendWhenProcessInstanceApproveReqDTO= new BpmMessageSendWhenProcessInstanceApproveReqDTO();
            bpmMessageSendWhenProcessInstanceApproveReqDTO.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
            bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceId(instance.getId());
            bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceName(instance.getName());
            bpmMessageSendWhenProcessInstanceApproveReqDTO.setStartUserNickname(sysUser.getNickName());
            bpmMessageSendWhenProcessInstanceApproveReqDTO.setReason(reason);
             return  bpmMessageSendWhenProcessInstanceApproveReqDTO;
        }

        default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason,  SysUser sysUser) {
              BpmMessageSendWhenProcessInstanceRejectReqDTO bpmMessageSendWhenProcessInstanceRejectReqDTO = new BpmMessageSendWhenProcessInstanceRejectReqDTO();
            bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceName(instance.getName());
            bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceId(instance.getId());
            bpmMessageSendWhenProcessInstanceRejectReqDTO.setReason(reason);
            bpmMessageSendWhenProcessInstanceRejectReqDTO.setStartUserNickname(sysUser.getNickName());
            bpmMessageSendWhenProcessInstanceRejectReqDTO.setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));

            return  bpmMessageSendWhenProcessInstanceRejectReqDTO;
        }

        default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,
                                                                                        List<HistoricTaskInstance> taskInstances,
                                                                                        BpmnModel bpmnModel,

                                                                                        Set<String> unfinishedTaskActivityIds,
                                                                                        Set<String> finishedTaskActivityIds,
                                                                                        Set<String> finishedSequenceFlowActivityIds,
                                                                                        Set<String> rejectTaskActivityIds,
                                                                                        Map<Long, SysUser> userMap,
                                                                                        Map<Long, DeptRespDTO> deptMap) {
            BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO();

            BpmProcessInstanceRespVO bpmProcessInstanceRespVO = BeanUtils.toBean(processInstance,BpmProcessInstanceRespVO.class );
            bpmProcessInstanceRespVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance));
            bpmProcessInstanceRespVO.setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap));
            // 基本信息
            respVO.setProcessInstance(bpmProcessInstanceRespVO);

            List<BpmTaskRespVO>bpmTaskRespVOList=new ArrayList<>();
            for (int i = 0; i < taskInstances.size(); i++) {
                HistoricTaskInstance task=  taskInstances.get(i);
                BpmTaskRespVO bpmTaskRespVO = new BpmTaskRespVO();
                bpmTaskRespVO=BeanUtils.toBean(task, BpmTaskRespVO.class);
                bpmTaskRespVO.setStatus(FlowableUtils.getTaskStatus(task));
                bpmTaskRespVO.setReason(FlowableUtils.getTaskReason(task));
                bpmTaskRespVO.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap));
                bpmTaskRespVO.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap));
                bpmTaskRespVOList.add(bpmTaskRespVO);
            }

            respVO.setTasks(bpmTaskRespVOList);

            respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel));

            // 进度信息
            respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds);
            respVO.setFinishedTaskActivityIds(finishedTaskActivityIds);
            respVO.setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds);
            respVO.setRejectedTaskActivityIds(rejectTaskActivityIds);;
            return respVO;
        }


    default UserSimpleBaseVO buildUser(String userIdStr,
                                       Map<Long, SysUser> userMap,
                                       Map<Long, DeptRespDTO> deptMap) {
        if (StrUtil.isEmpty(userIdStr)) {
            return null;
        }
        Long userId = NumberUtils.parseLong(userIdStr);
        return buildUser(userId, userMap, deptMap);
    }

    default UserSimpleBaseVO buildUser(Long userId,
                                       Map<Long, SysUser> userMap,
                                       Map<Long, DeptRespDTO> deptMap) {
        if (userId == null) {
            return null;
        }
        SysUser user = userMap.get(userId);
        if (user == null) {
            return null;
        }
        UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class);
        // DeptRespDTO dept = user.getDeptId() != null ? deptMap.get(user.getDeptId()) : null;
        DeptRespDTO dept=null;
        if (dept != null) {
            userVO.setDeptName(user.getDept().getDeptName());
        }
        return userVO;
    }

    default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) {
        if (task == null) {
            return null;
        }
        BpmApprovalDetailRespVO.ActivityNodeTask activityNodeTask = new BpmApprovalDetailRespVO.ActivityNodeTask();
        activityNodeTask =BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class);
        activityNodeTask.setStatus(FlowableUtils.getTaskStatus(task));
        activityNodeTask.setReason(FlowableUtils.getTaskReason(task));
        return  activityNodeTask;
    }

    default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
                                   List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
                                   BpmTaskRespVO todoTask) {
        Set<Long> userIds = new HashSet<>();
        if (processInstance != null) {
            userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
        }
        for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) {
            CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee));
            CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner));
            CollUtil.addAll(userIds, activityNode.getCandidateUserIds());
        }
        if (todoTask != null) {
            CollUtil.addIfAbsent(userIds, todoTask.getAssignee());
            CollUtil.addIfAbsent(userIds, todoTask.getOwner());
            if (CollUtil.isNotEmpty(todoTask.getChildren())) {
                CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee));
                CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner));
            }
        }
        return userIds;
    }

    default Set<Long> parseUserIds02(HistoricProcessInstance processInstance,
                                     List<HistoricTaskInstance> tasks) {
        Set<Long> userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId()));
        tasks.forEach(task -> {
            CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee())));
            CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner())));
        });
        return userIds;
    }

    default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel,
                                                        ProcessDefinition processDefinition,
                                                        HistoricProcessInstance processInstance,
                                                        Integer processInstanceStatus,
                                                        List<BpmApprovalDetailRespVO.ActivityNode> activityNodes,
                                                        BpmTaskRespVO todoTask,

                                                        Map<Long, SysUser> userMap,
                                                        Map<Long, DeptRespDTO> deptMap) {
        // 1.1 流程实例
        BpmProcessInstanceRespVO processInstanceResp = null;
        if (processInstance != null) {
            SysUser startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId()));
            // DeptRespDTO dept = startUser != null ? deptMap.get(startUser.getDeptId()) : null;
            DeptRespDTO dept =new DeptRespDTO();
            processInstanceResp = buildProcessInstance(processInstance, null, startUser, dept);
        }

        // 1.2 流程定义
        //        BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition(
        //                processDefinition, null, processDefinitionInfo, null, null, bpmnModel);

        // 1.3 流程节点
        activityNodes.forEach(approveNode -> {
            if (approveNode.getTasks() != null) {
                approveNode.getTasks().forEach(task -> {
                    task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap));
                    task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap));
                });
            }
            approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap)));
        });

        // 1.4 待办任务
        if (todoTask != null) {
            todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap));
            todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap));
            if (CollUtil.isNotEmpty(todoTask.getChildren())) {
                todoTask.getChildren().forEach(childTask -> {
                    childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap));
                    childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap));
                });
            }
        }

        // 2. 拼接起来
        BpmApprovalDetailRespVO  bpmApprovalDetailRespVO=   new BpmApprovalDetailRespVO();
        bpmApprovalDetailRespVO.setStatus(processInstanceStatus);
        // bpmApprovalDetailRespVO.setProcessDefinition(definitionResp);
        bpmApprovalDetailRespVO.setProcessInstance(processInstanceResp);
        //   bpmApprovalDetailRespVO.setFormFieldsPermission(formFieldsPermission);
        bpmApprovalDetailRespVO.setTodoTask(todoTask);
        bpmApprovalDetailRespVO.setActivityNodes(activityNodes);
        return  bpmApprovalDetailRespVO;
    }

}